using System;
using System.Collections.Generic;

using SharpGL.Media3D;

namespace SharpGL.SceneGraph.Evaluators
{
	/// <summary>
	/// This class represent's a grid of points, just like you'd get on a NURBS
	/// surface, or a patch.
	/// </summary>
	[Serializable()]
	public class VertexGrid
    {
        #region Protected Fields

        protected List<Point3D> vertices = new List<Point3D>();
        protected int x = 0;
        protected int y = 0;

        #endregion Protected Fields

        #region Public Methods

        public virtual void CreateGrid(int x, int y)
        {
            //	Clear the current array.
            vertices.Clear();

            //	Add a new set of control points.
            for (int yvals = 0; yvals < y; yvals++)
                for (int xvals = 0; xvals < x; xvals++)
                    vertices.Add(new Point3D(xvals, 1.0f, yvals));

            this.x = x;
            this.y = y;
        }

		/// <summary>
		/// Use this to draw the point grid.
		/// </summary>
		/// <param name="gl">OpenGL object.</param>
        /// <param name="points">Draw each individual point (with selection names).</param>
        /// <param name="lines">Draw the lines connecting the points.</param>
		public virtual void Draw(OpenGL gl, bool points, bool lines)
		{
			if (OpenGL.IsValidContext(gl))
            {
                //	Save the attributes.
                gl.PushAttrib(OpenGL.GL_ALL_ATTRIB_BITS);
                gl.Disable(OpenGL.GL_LIGHTING);
			    gl.Color(1, 0, 0, 1);

			    if(points)
			    {
                    int name = 0;

				    gl.PointSize(5);

				    //	Add a new name (the point name).
				    gl.PushName(0);

				    foreach(Point3D v in vertices)
				    {
					    //	Set the name, draw the point.
					    gl.LoadName((uint)name++);
					    //todo draw point
                        //((IInteractable)point).DrawPick(currentOpenGLContext);
				    }

				    //	Pop the name.
				    gl.PopName();
			    }

			    if(lines)
			    {
                    //	Draw lines along each row, then along each column.
                    gl.DepthFunc(OpenGL.GL_ALWAYS);

				    gl.LineWidth(1);
                    gl.Disable(OpenGL.GL_LINE_SMOOTH);

				    for(int col=0; col < y; col++)
				    {
					    for(int row=0; row < x; row++)
					    {
						    //	Create point indicies.
						    int nTopLeft = (col * x) + row;
						    int nBottomLeft = ((col + 1) * x) + row;

                            gl.Begin(OpenGL.GL_LINES);
						    if(row < (x-1))
						    {
							    gl.Vertex(vertices[nTopLeft]);
							    gl.Vertex(vertices[nTopLeft + 1]);
						    }
						    if(col < (y-1))
						    {
							    gl.Vertex(vertices[nTopLeft]);
							    gl.Vertex(vertices[nBottomLeft]);
						    }
						    gl.End();
					    }
				    }
                    gl.DepthFunc(OpenGL.GL_LESS);
			    }

			    gl.PopAttrib();
            }
		}

        /// <summary>
		/// This function returns all of the control points as a float array, which 
		/// is in the format [0] = point 1 X, [1] = point 1 Y, [2] = point 1 Z, 
		/// [3] = point 2 X etc etc... This array is suitable for OpenGL functions
		/// for evaluators and NURBS.
		/// </summary>
		/// <returns>An array of floats.</returns>
		public float[] ToFloatArray()
		{
			float[] floats = new float[Vertices.Count * 3];
			int index = 0;
			foreach(Point3D pt in Vertices)
			{
				floats[index++] = pt.X;
				floats[index++] = pt.Y;
				floats[index++] = pt.Z;
			}

			return floats;
		}

        #endregion Public Methods

        #region Public Properties

        public List<Point3D> Vertices
		{
			get {return vertices;}
			set
            {
                if (value != null)
                    vertices = value;
                else
                    vertices.Clear();
            }
		}
		
        public int Width
		{
			get {return x;}
			protected set {x = value;}
		}
		
        public int Height
		{
			get {return x;}
			protected set {y = value;}
        }

        #endregion Public Properties
    }
}